package cn.maihe.elg.operation.service.order;

import cn.maihe.elg.operation.centers.supports.CenterInterfaceService;
import cn.maihe.elg.operation.centers.supports.CenterInterfaceServiceFactory;
import cn.maihe.elg.operation.centers.supports.model.bo.GuaranteeClaimSettlementResultBo;
import cn.maihe.elg.operation.exception.ErrorCode;
import cn.maihe.elg.operation.exception.SystemRuntimeException;
import cn.maihe.elg.operation.model.dto.InnerBaseResp;
import cn.maihe.elg.operation.model.enums.*;
import cn.maihe.elg.operation.model.vo.order.ListRefundOrderDO;
import cn.maihe.elg.operation.model.vo.order.ListRefundOrderVO;
import cn.maihe.elg.operation.model.vo.order.RefundOrderDetailVO;
import cn.maihe.elg.operation.model.vo.order.RefundOrderStatusOperateVO;
import cn.maihe.elg.operation.repository.entity.*;
import cn.maihe.elg.operation.service.BaseDaoService;
import cn.maihe.elg.operation.service.business.ExchangeCenterService;
import cn.maihe.elg.operation.supports.system.ElgResourceService;
import cn.maihe.elg.operation.utils.DateUtil;
import cn.maihe.elg.operation.utils.ElgLogger;
import cn.maihe.elg.operation.utils.IdGenerate;
import cn.maihe.framework.starter.base.sql.PageBo;
import cn.maihe.framework.starter.mybatis.mapper.PlusEntityWrapper;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.formula.functions.Now;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @Description
 * @Author WJH
 * @Date 2021/05/12
 */
@Service
@Slf4j
public class RefundOrderInfoService extends BaseDaoService<RefundOrderInfoDO> {

    @Resource
    GuaranteeProgressInfoService guaranteeProgressInfoService;
    @Resource
    GuaranteeAttachmentInfoService guaranteeAttachmentInfoService;
    @Resource
    RefundOrderAttachmentService refundOrderAttachmentService;
    @Resource
    private ExchangeCenterService exchangeCenterService;
    @Resource
    private AcceptOrderInfoService acceptOrderInfoService;
    @Resource
    private NoticeRetryInfoService noticeRetryInfoService;
    @Resource
    private ElgResourceService elgResourceService;



    public PageBo<ListRefundOrderDO> selectPageBySql(int pageNo, int pageSize, ListRefundOrderVO vo) {
        Map<String, Object> paramsMap = toMapAndAddParams(vo);
        //订单时间
        if (vo.getGmtCreateRange() != null && vo.getGmtCreateRange().length == 2) {
            String[] rangeAy = vo.getGmtCreateRange();
            if (StringUtils.isNoneBlank(rangeAy[0], rangeAy[1])) {
                paramsMap.put("startGmtCreate", DateUtil.parseDate(rangeAy[0] + " 00:00:00", "yyyy-MM-dd HH:mm:ss"));
                paramsMap.put("endGmtCreate", DateUtil.parseDate(rangeAy[1] + " 23:59:59", "yyyy-MM-dd HH:mm:ss"));
            }
        }
        return this.selectPageBySql("selectRefundOrderList", "selectRefundOrderCount", paramsMap, pageNo, pageSize);
    }

    public RefundOrderDetailVO getRefundOrderDetailById(Long id) {
        Assert.notNull(id, () -> "Id不能为空");
        Map<String, Object> params = Maps.newHashMap();
        params.put("id", id);
        RefundOrderDetailVO refundOrderDetailVO = this.selectOneBySql("getRefundOrderDetailById", params);


        List<GuaranteeAttachmentInfoDO> aList = guaranteeAttachmentInfoService.getAllAttachmentByAcceptOrderNo(refundOrderDetailVO.getAcceptOrderNo());
        if (!CollectionUtils.isEmpty(aList)) {
            aList.forEach(l -> {
                if (AttachmentFileType.TYPE_0.getType() == l.getType()) {
                    refundOrderDetailVO.setAllOFDFile(l);
                } else if (AttachmentFileType.TYPE_1.getType() == l.getType()) {
                    refundOrderDetailVO.setOrigApplyFile(l);
                } else if (AttachmentFileType.TYPE_2.getType() == l.getType()) {
                    refundOrderDetailVO.setOrgQualificationFile(l);
                } else if (AttachmentFileType.TYPE_3.getType() == l.getType()) {
                    refundOrderDetailVO.setOrgBusinessLicFile(l);
                } else if (AttachmentFileType.TYPE_4.getType() == l.getType()) {
                    refundOrderDetailVO.setOpenAccountFile(l);
                } else if (AttachmentFileType.TYPE_5.getType() == l.getType()) {
                    refundOrderDetailVO.setPayProveFile(l);
                } else if (AttachmentFileType.TYPE_6.getType() == l.getType()) {
                    refundOrderDetailVO.setReceiveProveFile(l);
                } else if (AttachmentFileType.TYPE_7.getType() == l.getType()) {
                    refundOrderDetailVO.setGuaranteeFile(l);
                } else if (AttachmentFileType.TYPE_8.getType() == l.getType()) {
                    refundOrderDetailVO.setInvoiceFile(l);
                } else if (AttachmentFileType.TYPE_9.getType() == l.getType()) {
                    refundOrderDetailVO.setOtherFile(l);
                }
            });
        }
        return refundOrderDetailVO;
    }

    public RefundOrderInfoDO getByGuaranteeNo(String guaranteeIno) {
        return this.selectOne(RefundOrderInfoDOCondition.builder().andGuaranteeNoEq(guaranteeIno).orderDesc(RefundOrderInfoDO::getGmtCreate).build());
    }

    public RefundOrderInfoDO getByGuaranteeNoAndRefundOrderNo(String guaranteeIno, String refundOrderNo) {
        return this.selectOne(RefundOrderInfoDOCondition.builder().andGuaranteeNoEq(guaranteeIno).andRefundOrderNoEq(refundOrderNo).orderDesc(RefundOrderInfoDO::getGmtCreate).build());
    }

    public RefundOrderInfoDO getByApplyNo(String applyNo) {
        return this.selectOne(RefundOrderInfoDOCondition.builder().andRefundOrderNoEq(applyNo).orderDesc(RefundOrderInfoDO::getGmtCreate).build());
    }

    /**
     * 理赔申请接口处理
     *
     * @param infoDO
     */
    public void insertByClaimSettlement(ApplyOrderInfoDO infoDO) {
        RefundOrderInfoDO curDo = RefundOrderInfoDO.builder()
                .id(IdGenerate.getId()).centerId(infoDO.getCenterId())
                .orgId(infoDO.getOrgId()).guaranteeNo(infoDO.getGuaranteeNo())
                .centerOrderId(infoDO.getCenterOrderId()).acceptOrderNo(infoDO.getAcceptOrderNo())
                .applyOrderNo(infoDO.getApplyOrderNo()).refundOrderNo(IdGenerate.genRefundOrderNo())
                .guaranteeType(infoDO.getGuaranteeType()).guaranteeCategory(infoDO.getGuaranteeCategory())
                .guaranteeFee(infoDO.getGuaranteeFee()).projectId(infoDO.getProjectId()).projectName(infoDO.getProjectName())
                .sectionName(infoDO.getSectionName())
                .bidCustomerId(infoDO.getBidCustomerId()).bidCustomerName(infoDO.getBidCustomerName())
                .bidIndate(infoDO.getBidIndate()).bidBond(infoDO.getBidBond())
                .institutionId(infoDO.getInstitutionId()).institutionName(infoDO.getInstitutionName())
                .attachmentId(infoDO.getAttachmentId()).remark("申请理赔退款")
                .gmtCreate(Date.from(Instant.now())).gmtModified(Date.from(Instant.now())).userCreate("接口申请")
                .build();

        this.insert(curDo);

    }

    /**
     * 理赔申请接口处理
     *
     * @param infoDO
     */
    public void insertByClaimSettlement(AcceptOrderInfoDO infoDO) {
        RefundOrderInfoDO curDo = RefundOrderInfoDO.builder()
                .id(IdGenerate.getId()).centerId(infoDO.getCenterId())
                .orgId(infoDO.getOrgId()).guaranteeNo(infoDO.getGuaranteeNo())
                .centerOrderId(infoDO.getCenterOrderId()).acceptOrderNo(infoDO.getAcceptOrderNo())
                .applyOrderNo(infoDO.getApplyOrderNo()).refundOrderNo(IdGenerate.genRefundOrderNo())
                .guaranteeType(infoDO.getGuaranteeType()).guaranteeCategory(infoDO.getGuaranteeCategory())
                .guaranteeFee(infoDO.getGuaranteeFee()).projectId(infoDO.getProjectId()).projectName(infoDO.getProjectName())
                .sectionName(infoDO.getSectionName())
                .bidCustomerId(infoDO.getBidCustomerId()).bidCustomerName(infoDO.getBidCustomerName())
                .bidIndate(infoDO.getBidIndate()).bidBond(infoDO.getGuaranteeFee())
                .institutionId(infoDO.getInstitutionId())
//                .institutionName(infoDO.getInstitutionName())
                .attachmentId(infoDO.getAttachmentId()).remark("申请理赔退款")
                .gmtCreate(Date.from(Instant.now())).gmtModified(Date.from(Instant.now())).userCreate("接口申请")
                .build();

        this.insert(curDo);

    }

    /**
     * 待退款  退款初审操作
     */
    @Transactional(rollbackFor = Exception.class)
    public Boolean firstAudit(RefundOrderInfoDO refundOrderInfo, RefundOrderStatusOperateVO vo) {
//        RefundOrderStatus nextStatus = vo.getAuditStatus() == 1 ? RefundOrderStatus.APPROVAL : RefundOrderStatus.REFUSED;
//        RefundOrderInfoDO updateDo = RefundOrderInfoDO.builder().id(vo.getId()).status(nextStatus.getStatus()).remark(vo.getAuditRemark()).build();
//        this.updateById(updateDo);
//
//        if (nextStatus == RefundOrderStatus.REFUSED) {
//            AcceptOrderInfoDO updateAcceptOrder = AcceptOrderInfoDO.builder()
//                    .claimsStatus(ClaimsStatus.FAILURE.getStatus())
//                    .claimsRemark(vo.getAuditRemark())
//                    .build();
//            acceptOrderInfoService.updateByAcceptOrderNo(updateAcceptOrder, refundOrderInfo.getAcceptOrderNo());
//        }
//        this.pushRefundOrderInfo(updateDo);

        //没有符合审核-复核操作，直接审核通过或拒绝
        confirmAudit(refundOrderInfo, vo);
        return true;
    }

    /**
     * 待退款  退款初审操作
     */
    @Transactional(rollbackFor = Exception.class)
    public Boolean confirmAudit(RefundOrderInfoDO refundOrderInfo, RefundOrderStatusOperateVO vo) {
//        RefundOrderStatus nextStatus = vo.getAuditStatus() == 1 ? RefundOrderStatus.REFUNDED : RefundOrderStatus.REFUSED;
        switch (RefundAuditStatus.resolve(vo.getAuditStatus())) {
            case NO_PAYOUTS:
                refundOrderInfo.setStatus(RefundOrderStatus.REFUSED.getStatus());
                break;
            case PAID:
                refundOrderInfo.setStatus(RefundOrderStatus.REFUNDED.getStatus());
                break;
            case NOT_PASSED:
                refundOrderInfo.setStatus(RefundOrderStatus.NO_PASSED.getStatus());
                break;
            default:
                ;break;
        }
//        refundOrderInfo.setStatus(nextStatus.getStatus());
        refundOrderInfo.setRemark(vo.getAuditRemark());
        if (vo.getAuditStatus() != null) {
            // 理赔成功数据录入
            refundOrderInfo.setCompensateDate(vo.getCompensateDate());
            refundOrderInfo.setCompensateWay(vo.getCompensateWay());
            refundOrderInfo.setTransferDate(vo.getTransferDate());
            //赔付金额单位:元
            refundOrderInfo.setPayerAmount(vo.getCompensateAmount());
            refundOrderInfo.setTransferFileList(vo.getTransferFileList());

            // --- 开始处理附件的同步 ---
            PlusEntityWrapper<RefundOrderAttachmentDO> queryWrapper = new PlusEntityWrapper<>(RefundOrderAttachmentDO.class);
            queryWrapper.eq("refund_order_id", refundOrderInfo.getId());
            queryWrapper.eq("business_type", 2);
            List<RefundOrderAttachmentDO> oldAttachments = refundOrderAttachmentService.selectList(queryWrapper);

            List<RefundOrderAttachmentDO> newAttachments = new ArrayList<>();
            Set<String> newFilePaths = new HashSet<>();
            if (StringUtils.isNotBlank(vo.getTransferFileList())) {
                JSONArray fileList = JSON.parseArray(vo.getTransferFileList());
                if (fileList != null && !fileList.isEmpty()) {
                    newAttachments = this.buildAttachmentsFromUrl(refundOrderInfo, fileList);
                    for (RefundOrderAttachmentDO attachment : newAttachments) {
                        newFilePaths.add(attachment.getFilePath());
                    }
                }
            }
            if (oldAttachments != null && !oldAttachments.isEmpty()) {
                for (RefundOrderAttachmentDO oldAttachment : oldAttachments) {
                    if (!newFilePaths.contains(oldAttachment.getFilePath())) {
                        try {
                            log.info("准备删除过期的理赔附件: {}", oldAttachment.getFilePath());
                            elgResourceService.deleteFileByRelativePath(oldAttachment.getFilePath());
                        } catch (Exception e) {
                            log.error("删除旧的赔付附件物理文件失败，路径: {}", oldAttachment.getFilePath(), e);
                        }
                    }
                }
            }
            PlusEntityWrapper<RefundOrderAttachmentDO> deleteWrapper = new PlusEntityWrapper<>(RefundOrderAttachmentDO.class);
            deleteWrapper.eq("refund_order_id", refundOrderInfo.getId());
            deleteWrapper.eq("business_type", 2);
            refundOrderAttachmentService.delete(deleteWrapper);

            if (!newAttachments.isEmpty()) {
                refundOrderAttachmentService.insert(newAttachments, newAttachments.size());
            }
        }
        this.updateById(refundOrderInfo);

        AcceptOrderInfoDO updateAcceptOrder = AcceptOrderInfoDO.builder().claimsRemark(vo.getAuditRemark()).build();
        if (refundOrderInfo.getStatus() == RefundOrderStatus.REFUSED.getStatus()) {
            updateAcceptOrder.setClaimsStatus(ClaimsStatus.FAILURE.getStatus());
        }else if (refundOrderInfo.getStatus() == RefundOrderStatus.NO_PASSED.getStatus()){
            updateAcceptOrder.setClaimsStatus(ClaimsStatus.NO_PASSED.getStatus());
        }
        else if (refundOrderInfo.getStatus() == RefundOrderStatus.REFUNDED.getStatus()) {
            updateAcceptOrder.setStatus(AcceptOrderStatus.FINISH.getStatus());
            updateAcceptOrder.setClaimsStatus(ClaimsStatus.SUCCESS.getStatus());
        }
        acceptOrderInfoService.updateByAcceptOrderNo(updateAcceptOrder, refundOrderInfo.getAcceptOrderNo());

        //异步通知中心理赔结果
        this.noticeClaimResult(refundOrderInfo);
        return true;
    }

    private List<RefundOrderAttachmentDO> buildAttachmentsFromUrl(RefundOrderInfoDO refundOrderInfo, JSONArray fileList) {
        List<RefundOrderAttachmentDO> attachmentsToSave = new ArrayList<>();
        for (int idx = 0; idx < fileList.size(); idx++) {
            JSONObject file = fileList.getJSONObject(idx);
            try {
                String fileUrl = file.getString("fileUrl");
                URL url = new URL(fileUrl);
                String query = url.getQuery();
                String relativePath = null;
                if (StringUtils.isNotBlank(query)) {
                    String[] params = query.split("&");
                    for (String param : params) {
                        if (param.startsWith("relativeURI=")) {
                            relativePath = param.substring("relativeURI=".length());
                            break;
                        }
                    }
                }
                if (relativePath == null) {
                    throw new IllegalArgumentException("无法从URL中解析出文件的相对路径(relativeURI)");
                }

                Path absolutePath = elgResourceService.getAbsolutePath(relativePath);
                if (!Files.exists(absolutePath)) {
                    log.error("凭证文件丢失，请重新上传。路径: {}", absolutePath);
                    throw new SystemRuntimeException(ErrorCode.ERROR, "凭证文件丢失，请重新上传");
                }

                String fileType = elgResourceService.getFileType(absolutePath);

                String fileName = absolutePath.getFileName().toString();
                String filePath = "/" +elgResourceService.relativePath(absolutePath, "ofd", refundOrderInfo.getAcceptOrderNo(), fileName);
                String fileMd5 = elgResourceService.getFileMD5AsHex(absolutePath);
                RefundOrderAttachmentDO attachment = RefundOrderAttachmentDO.builder()
                        .refundOrderId(refundOrderInfo.getId())
                        .no(idx)
                        .businessType(2)
                        .fileUrl(fileUrl)
                        .filePath(filePath)
                        .fileType(fileType)
                        .fileName(fileName)
                        .fileMd5(fileMd5)
                        .gmtCreate(Date.from(Instant.now()))
                        .gmtModified(Date.from(Instant.now()))
                        .build();
                attachmentsToSave.add(attachment);
            } catch (Exception e) {
                log.error("处理赔付转账凭证文件异常, URL: {}, 错误: {}", file.getString("fileUrl"), e.getMessage(), e);
                throw new SystemRuntimeException(ErrorCode.ERROR, "处理转账凭证失败: " + e.getMessage());
            }
        }
        return attachmentsToSave;
    }

    /**
     * 通知理赔结果
     *
     * @param refundOrderInfo 理赔记录
     */
    private void noticeClaimResult(RefundOrderInfoDO refundOrderInfo) {
        CompletableFuture.runAsync(() -> {
            GuaranteeClaimSettlementResultBo bo = new GuaranteeClaimSettlementResultBo();
            bo.setRefundOrderInfo(refundOrderInfo);
            bo.setAcceptOrderNo(refundOrderInfo.getAcceptOrderNo());
            try {
                CenterNoEnum centerNoEnum = exchangeCenterService.getCenterNoEnumByAcceptOrderNo(refundOrderInfo.getAcceptOrderNo());
                CenterInterfaceService cService = CenterInterfaceServiceFactory.getByCenterNoEnum(centerNoEnum);

                InnerBaseResp innerBaseResp = cService.noticeGuaranteeClaimSettlementResult(bo);
                if (innerBaseResp != null && innerBaseResp.getRespCode().isSuccess()) {
                    ElgLogger.info(log, log -> log.info("受理订单号[{}] >> 异步推送理赔结果成功：响应报文：{}", refundOrderInfo.getAcceptOrderNo(), JSON.toJSONString(innerBaseResp)));
                } else {
                    noticeRetryInfoService.saveOrUpdateNoticeRetryInfo(NoticeTypeEnum.claims_apply_result,
                            refundOrderInfo.getCenterId(), refundOrderInfo.getOrgId(),
                            refundOrderInfo.getAcceptOrderNo(), bo, null, innerBaseResp
                    );
                    ElgLogger.info(log, log -> log.info("受理订单号[{}] >> 异步推送理赔结果失败,待下次通知：响应报文：{}", refundOrderInfo.getAcceptOrderNo(), JSON.toJSONString(innerBaseResp)));
                }
            } catch (Exception e) {
                InnerBaseResp innerBaseResp = InnerBaseResp.exception(e.getMessage());
                ElgLogger.info(log, log -> log.info("受理订单号[{}] >> 异步推送理赔结果异常：{}", refundOrderInfo.getAcceptOrderNo(), e.getMessage(), e));
                noticeRetryInfoService.saveOrUpdateNoticeRetryInfo(NoticeTypeEnum.claims_apply_result,
                        refundOrderInfo.getCenterId(), refundOrderInfo.getOrgId(),
                        refundOrderInfo.getAcceptOrderNo(), bo, null, innerBaseResp
                );
            }
        });
    }

    @Transactional(rollbackFor = Exception.class)
    public void saveOrder(RefundOrderInfoDO refundOrderInfoDO, List<RefundOrderAttachmentDO> refundOrderAttachmentDOS) {
        this.insert(refundOrderInfoDO);
        if (!CollectionUtils.isEmpty(refundOrderAttachmentDOS)) {
            refundOrderAttachmentService.insert(refundOrderAttachmentDOS, 100);
        }
    }
}
